package com.ht.callback.wexinaes;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 微信公众号消息解密工具类
 * @author asq
 * @createTime 2025年4月8日 17:23:18
 */
public class WeChatMessageDecryptor {
	public static void main(String[] args) {
		String str = "WkR6EF3jt0kCNtpiHnHq2bykFh8/5nguEUbZOVbKA3EVzeusirgN5zklwJK/mNVHQvpZLr0HzZe0XhfJLtT78bGo3H7crHP7X3h/kdsVy2w5bm43aw1rNxOT7aw+NZ66i6jUPEX+WdpvlvWGVrgebSYP5/7wxKAiFM2t3hfJAekSVE3cxbTqx1B4CbeJG3LXJQLzUhJvdG2PFY/wAWX0aYPXWRr8bAReboH/xPGtkbxzcoqqMUC8Q39pZVP8QR1DVDPAFfs8IEICXnbJ11xnKMPrhuwTQSe70icetJ7spjg0Gb3DgyciT5Z/f2wVfvcQcrBbsnl7lsIKnHX6xRFooLgpRGJuSdQLR4fRJM4tyI+yL1CXoB+z1MfcQp04zxZUmMBW7VzrY0Hbg6uCL5J8a3HzqTj+tIeJOi7wzADH/fG2IbjEYH4rs6HsqBtzcLsTmoB39cYBwPGs1Q0GEHHFQs8pZuAeZJDoE0SLVqxTKDdlbCqDNr9f44o8+kgqR0rh45qARLb5/lIuyRgPIW8JiYi8xfCgqH0T28vWGJZAtdS2LZybxsaV5k1x9rh4JsS9/Kcy61urYkS58nbvN/ce8PbH0mpgXC64GfbjnDxbhLYatr/OmyuAOpVld5zLzT0n9gN5NOlJ4N9r47MEHBgKnyrh0gPwu60TJzKkkOPMEGfJ1/NiOrj3d6njKAHEXY9M7IduN5LDIVQHoo2Fbm4YiI/frGCTgus0LP/ZmYLc0lUy42/ilq1Pmz2V8Rbu1F/R";
		try {
			System.err.println(decryptMsg("NiRy2l1jerdPcBGat7uuTHKQ2JnBUbpD", "DnHjIS3fYUJ1T3Vsg1muWZXZcz1NTB2SykDyeNisXg8", 
					"31ab1bf1356150d5ab0b21ec6765f3d35590c59d", "1744288876", "2025240773", str));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 解密公众号消息
	 * @param token
	 * @param encodingAesKey
	 * @param msgSignature
	 * @param timeStamp
	 * @param nonce
	 * @param encryptMsg
	 * @return
	 * @throws Exception
	 */
	public static String decryptMsg(String token, String encodingAesKey, String msgSignature, String timeStamp,
	        String nonce, String encryptMsg) throws Exception {
	    // 1. 验证签名
	    String calculatedSignature = getSignature(token, timeStamp, nonce, encryptMsg);
	    if (!calculatedSignature.equals(msgSignature)) {
	        throw new RuntimeException("签名验证失败");
	    }

	    // 2. 准备AES密钥
	    byte[] aesKey = Base64.getDecoder().decode(padBase64(encodingAesKey));
	    if (aesKey.length != 32) {
	        throw new IllegalArgumentException("非法AES密钥");
	    }
	    
	    SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
	    IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));

	    // 3. 解密
	    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");  // 改为 NoPadding 自己处理填充
	    cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
	    byte[] encrypted = Base64.getDecoder().decode(encryptMsg);
	    byte[] original = cipher.doFinal(encrypted);

	    // 4. 处理填充
	    byte[] bytes = PKCS7Padding.removePadding(original);

	    // 分离: 16字节随机字符串 + 4字节消息长度 + 消息内容 + appId
	    byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
	    int xmlLength = bytesToInt(networkOrder);

	    String xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), StandardCharsets.UTF_8);
	    return xmlContent;
	}

	// Base64补全方法
	private static String padBase64(String key) {
	    int pad = key.length() % 4;
	    if (pad > 0) {
	        return key + "====".substring(pad);
	    }
	    return key;
	}

	// PKCS7填充处理
	public static class PKCS7Padding {
	    public static byte[] removePadding(byte[] decrypted) {
	        int pad = decrypted[decrypted.length - 1];
	        if (pad < 1 || pad > 32) {
	            throw new IllegalArgumentException("无效的PKCS7填充");
	        }
	        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
	    }
	}

	private static String getSignature(String token, String timestamp, String nonce, String encrypt) throws Exception {
		String[] arr = new String[] { token, timestamp, nonce, encrypt };
		Arrays.sort(arr);

		StringBuilder content = new StringBuilder();
		for (String s : arr) {
			content.append(s);
		}

		MessageDigest md = MessageDigest.getInstance("SHA-1");
		byte[] digest = md.digest(content.toString().getBytes(StandardCharsets.UTF_8));

		StringBuilder hexStr = new StringBuilder();
		for (byte b : digest) {
			String hex = Integer.toHexString(b & 0xFF);
			if (hex.length() == 1) {
				hexStr.append('0');
			}
			hexStr.append(hex);
		}
		return hexStr.toString();
	}

	private static int bytesToInt(byte[] bytes) {
		int value = 0;
		for (int i = 0; i < 4; i++) {
			value <<= 8;
			value |= bytes[i] & 0xFF;
		}
		return value;
	}
}
